﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;

namespace WebDiagnostics.Diagnosers.LoadedAssemblies
{
    [DiagnoserDescriptor("Currently Loaded Assemblies")]
    public class LoadedAssembliesDiagnoser : IDiagnoser
    {
        protected static Regex IncludedAssembliesRegex =
            new Regex("^(?!System|Microsoft|CppCodeProvider|mscorlib|Anonymously Hosted DynamicMethods Assembly|WebDev.WebHost)");

        public Diagnosis Diagnose(HttpContextBase context)
        {
            IEnumerable<dynamic> assemblyInfos = 
                AppDomain.CurrentDomain.GetAssemblies()
                    .Select(x => new { Assembly = x, References = x.GetReferencedAssemblies() });

            IEnumerable<LoadedAssemblyInfo> loadedAssemblies = GetAssemblyInfo(assemblyInfos).ToArray();

            return new LoadedAssembliesDiagnosis(loadedAssemblies);
        }

        private static IEnumerable<LoadedAssemblyInfo> GetAssemblyInfo(IEnumerable<dynamic> assemblies)
        {
            if (assemblies == null)
                return Enumerable.Empty<LoadedAssemblyInfo>();

            IEnumerable<LoadedAssemblyInfo> assemblyInfos =
                from assemblyInfo in assemblies
                let assembly = (assemblyInfo is AssemblyName) ? null : (Assembly)assemblyInfo.Assembly
                let assemblyName = (assemblyInfo as AssemblyName) ?? assembly.GetName()
                let references = (assemblyInfo is AssemblyName) ? null : assemblyInfo.References
                let isDebugVersion = IsDebugVersion(assembly)
                let version = GetVersion(assemblyName, isDebugVersion)
                where IncludedAssembliesRegex.IsMatch(assemblyName.Name)
                select new LoadedAssemblyInfo
                           {
                               FullName = assemblyName.FullName,
                               Name = assemblyName.Name,
                               Version = assemblyName.Version.ToString(),
                               References = GetAssemblyInfo(references)
                           };
            
            return assemblyInfos;
        }

        private static string GetVersion(AssemblyName assemblyName, bool? isDebugVersion)
        {
            var version = assemblyName.Version.ToString();

            if (isDebugVersion == true)
                version += " [DEBUG]";

            return version;
        }

        private static bool? IsDebugVersion(Assembly assembly)
        {
            if(assembly == null)
                return null;

            DebuggableAttribute debuggableAttribute =
                assembly
                    .GetCustomAttributes(typeof (DebuggableAttribute), false)
                    .Cast<DebuggableAttribute>()
                    .SingleOrDefault();

            bool? isDebugVersion = 
                   debuggableAttribute != null 
                && debuggableAttribute.IsJITTrackingEnabled;

            return isDebugVersion;
        }
    }
}